use common::error::{with_context, SysError};
use serde::{Deserialize, Serialize};
use serde_json::{json, Map, Value};

#[derive(Serialize, Deserialize, Debug)]
struct Api {
    name: String,
    method: String,
    path: String,
    request: Value,
    response: Value,
}

#[derive(Serialize, Deserialize, Debug)]
struct PropType {
    #[serde(rename = "type")]
    type1: String,
    #[serde(default)]
    required: bool,
}

pub fn convert_swagger_to_api(
    swagger_content: String,
) -> Result<Value, Box<dyn std::error::Error>> {
    let swagger_doc: Map<String, Value> = serde_json::from_str(&swagger_content)?;

    let paths = swagger_doc
        .get("paths")
        .ok_or_else(|| SysError::msg("Missing 'paths' in swagger document"))?;
    let definitions = &swagger_doc
        .get("definitions")
        .ok_or_else(|| SysError::msg("Missing 'definitions' in swagger document"))?;

    let mut apis = Vec::new();

    for (path, methods) in paths.as_object().unwrap_or(&Map::new()).iter() {
        for (method, details) in methods.as_object().unwrap().iter() {
            // tag1
            let request = extract_request(details, definitions)?;
            let response = extract_response(details, definitions)?;

            apis.push(Api {
                name: details["operationId"]
                    .as_str()
                    .unwrap_or_default()
                    .to_string(),
                method: method.to_uppercase(),
                path: path.to_string(),
                request: json!({
                    "type": Value::String("object".to_string()),
                    "properties": request
                }),
                response: json!({
                    "type":Value::String("object".to_string()),
                    "properties": response
                }),
            });
        }
    }
    let mut map = Map::new();
    map.insert(
        "apis".to_string(),
        Value::Array(
            apis.into_iter()
                .map(serde_json::to_value)
                .collect::<Result<Vec<_>, _>>()
                .map_err(|err| with_context(err, "convert_swagger_to_api error"))?,
        ),
    );

    Ok(Value::Object(map))
}

fn build_props(val: &Value) -> Result<Value, Box<dyn std::error::Error>> {
    let mut result = Map::new();

    if let Value::Object(obj) = val {
        for (key, value) in obj.iter() {
            result.insert(
                key.clone(),
                serde_json::to_value(
                    serde_json::from_value::<PropType>(value.clone())
                        .map_err(|err| with_context(err, "build_props from value"))?,
                )
                .map_err(|err| with_context(err, "build_props to value"))?,
            );
        }
    }

    Ok(Value::Object(result))
}

fn extract_request(
    details: &Value,
    definitions: &Value,
) -> Result<Value, Box<dyn std::error::Error>> {
    let parameters = details
        .get("parameters")
        .ok_or_else(|| SysError::msg("Missing 'parameters' in API details"))?;

    if let Value::Array(params) = parameters {
        for param in params {
            if param["in"] == "body" {
                if let Value::String(origin_ref) = &param["schema"]["originalRef"] {
                    let definition = &definitions[origin_ref];
                    if let Value::String(type_val) = &definition["type"] {
                        if type_val == "object" {
                            return build_props(&definition["properties"]);
                        }
                    }
                }
            }
        }
    }

    Err(SysError::msg(&format!("无效的节点定义:{:?}", details)).into())
}

fn extract_response(
    details: &Value,
    definitions: &Value,
) -> Result<Value, Box<dyn std::error::Error>> {
    let responses = details
        .get("responses")
        .ok_or_else(|| SysError::msg("Missing 'responses' in API details"))?;

    if let Value::String(origin_ref) = &responses["200"]["schema"]["originalRef"] {
        let definition = &definitions[origin_ref];
        if let Value::String(type_val) = &definition["type"] {
            if type_val == "object" {
                return build_props(&definition["properties"]);
            }
        }
    }
    Err(SysError::msg(&format!("无效的节点定义:{:?}", details)).into())
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;
    use std::fs;

    #[test]
    fn test_convert_swagger_to_api() -> Result<(), Box<dyn std::error::Error>> {
        let swagger_content = fs::read_to_string("tests/swagger_api_doc.json")?;
        let api_json = convert_swagger_to_api(swagger_content)?;
        let expected_content = fs::read_to_string("tests/expected_api.json")?;
        let expected_json: Value = serde_json::from_str(&expected_content)?;

        assert_eq!(api_json, expected_json);
        Ok(())
    }

    #[test]
    fn test_build_props() {
        let input_json = json!({
            "chainCode": {
                "type": "string",
                "description": "链路编码",
                "refType": null
            },
            "endTime": {
                "type": "integer",
                "format": "int64",
                "description": "结束时间",
                "refType": null
            },
            // ... 其他字段 ...
            "vid": {
                "type": "string",
                "description": "vid",
                "refType": null
            }
        });

        let expected_json = json!({
            "chainCode": {
                "type": "string",
                "required": false
            },
            "endTime": {
                "type": "integer",
                "required": false
            },
            "vid": {
                "type": "string",
                "required": false
            }
        });

        let result = build_props(&input_json).expect("build_props error");
        assert_eq!(result, expected_json);
    }
}
